/**
 *  @brief  SHT30温湿度传感器相关，使用模拟IIC进行数据的读取
 */
#include <stdint.h>
#include "sht30.h"
#include "Delay.h"
#define SHT40 
// #define SHT30 
/**
 *  @brief  crc8校验函数，多项式为 x^8 + x^5 + x^4 + 1
 *  @param  data 要校验的数据
 *  @param  len 要校验的数据的字节数
 *  @retval 校验结果
 *  @note   该校验适合SHT3温湿度传感器的数据校验
 */
static uint8_t Crc8(uint8_t *data, int len)
{
    const uint8_t POLYNOMIAL = 0x31;
    uint8_t crc = 0xFF;
    int i, j;
 
    for (i = 0; i < len; ++i)
    {
        crc ^= *data++;
 
        for (j = 0; j < 8; ++j)
        {
            crc = (crc & 0x80) ? (crc << 1) ^ POLYNOMIAL : (crc << 1);
        }
    }
 
    return crc;
}
 
/**
 *  @brief  i2c的延时函数，延时时间要 > 4us
 *  @param  无
 *  @retval 无
 *  @note   可用逻辑分析仪测量I2C通讯时的频率工作条件
 */
static void i2c_delay(void)
{
    int i;
 
    for (i = 0; i < 10; i++)
    {
        Delay_us(1);
    }
}
 
/**
 *  @brief  i2c SCL 拉高
 *  @param  无
 *  @retval 无
 */
static void i2c_scl_high(void)
{
    GPIO_SetBits(SHT30_SCL_PORT, SHT30_SCL_PIN);
}
 
/**
 *  @brief  i2c SCL 拉低
 *  @param  无
 *  @retval 无
 */
static void i2c_scl_low(void)
{
    GPIO_ResetBits(SHT30_SCL_PORT, SHT30_SCL_PIN);
}
 
/**
 *  @brief  i2c SDA 拉高
 *  @param  无
 *  @retval 无
 */
static void i2c_sda_high(void)
{
    GPIO_SetBits(SHT30_SDA_PORT, SHT30_SDA_PIN);
}
 
/**
 *  @brief  i2c SDA 拉低
 *  @param  无
 *  @retval 无asm
 */
static void i2c_sda_low(void)
{
    GPIO_ResetBits(SHT30_SDA_PORT, SHT30_SDA_PIN);
}
 
/**
 *  @brief  读取SDA线上的值
 *  @param  无
 *  @retval 读取到的值，0或1
 */
static uint8_t i2c_read_sda_value(void)
{
    return GPIO_ReadInputDataBit(SHT30_SDA_PORT, SHT30_SDA_PIN);
    ;
}
 
/**
 *  @brief  i2c的起始信号
 *  @param  无
 *  @retval 无
 */
static void i2c_start(void)
{
    /* 当SCL高电平时，SDA出现一个下跳沿表示I2C总线启动信号 */
    i2c_scl_high();
    i2c_sda_high();
    i2c_delay();
 
    i2c_sda_low();
    i2c_delay();
 
    i2c_scl_low();
    i2c_delay();
}
 
/**
 *  @brief  i2c的停止信号
 *  @param  无
 *  @retval 无
 */
static void i2c_stop(void)
{
    /* 当SCL高电平时，SDA出现一个上跳沿表示I2C总线停止信号 */
    i2c_sda_low();
    i2c_scl_high();
    i2c_delay();
 
    i2c_sda_high();
}
 
/**
 *  @brief  i2c 发送一个字节
 *  @param  要发送的数据
 *  @retval 无
 */
void i2c_write_byte(uint8_t data)
{
    uint8_t i;
 
    i2c_scl_low();
    i2c_delay();
 
    /* 先发送字节的高位 bit7 */
    for (i = 0; i < 8; i++)
    {
        if (data & 0x80)
        {
            i2c_sda_high();
        }
        else
        {
            i2c_sda_low();
        }
        i2c_delay();
 
        i2c_scl_high();
        i2c_delay();
        i2c_scl_low();
 
        if (i == 7)
        {
            i2c_sda_high(); // 释放总线
        }
        data <<= 1; // 左移一个bit
        i2c_delay();
    }
}
 
/**
 *  @brief  i2c 读取一个字节
 *  @param  无
 *  @retval 无
 */
uint8_t i2c_read_byte(void)
{
    uint8_t i;
    uint8_t value;
 
    /* 读到第1个bit为数据的bit7 */
    value = 0;
    for (i = 0; i < 8; i++)
    {
        value <<= 1;
        i2c_scl_high();
        i2c_delay();
        if (i2c_read_sda_value() != 0)
        {
            value++;
        }
 
        i2c_scl_low();
        i2c_delay();
    }
 
    return value;
}
 
/**
 *  @brief  CPU等待从设备的应答信号
 *  @param  无
 *  @retval 0表示正确应答，1表示无应答
 */
static uint8_t i2c_wait_ack(void)
{
    uint8_t ret;
 
    i2c_sda_high(); /* CPU释放SDA总线 */
    i2c_delay();
 
    i2c_scl_high(); /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
    i2c_delay();
    if (i2c_read_sda_value() == 1) /* CPU读取SDA口线状态 */
    {
        ret = 1;
    }
    else
    {
        ret = 0;
    }
 
    i2c_scl_low();
    i2c_delay();
 
    return ret;
}
 
/**
 *  @brief  CPU产生一个ACK信号
 *  @param  无
 *  @retval 无
 */
static void i2c_ack(void)
{
    i2c_sda_low(); /* CPU驱动SDA = 0 */
    i2c_delay();
 
    i2c_scl_high(); /* CPU产生1个时钟 */
    i2c_delay();
 
    i2c_scl_low();
    i2c_delay();
 
    i2c_sda_high(); /* CPU释放SDA总线 */
}
 
/**
 *  @brief  CPU产生一个 NACK 信号
 *  @param  无
 *  @retval 无
 */
static void i2c_Nack(void)
{
    i2c_sda_high(); /* CPU驱动SDA = 1 */
    i2c_delay();
 
    i2c_scl_high(); /* CPU产生1个时钟 */
    i2c_delay();
 
    i2c_scl_low();
    i2c_delay();
}
 
/**
 *  @brief  i2c 发送多个字节
 *  @param  要发送的从机的地址
 *  @param  指向要写入的的数据的指针
 *  @param  要写入的数据的长度
 *  @retval 成功返回0，失败返回-1
 */
int i2c_write_bytes(uint8_t addr, uint8_t *write_buff, uint8_t buff_size)
{
    uint16_t i, j;
 
    // 1. 发送一个停止信号
    i2c_stop();
 
    /* 通过检查器件应答的方式，判断内部写操作是否完成, 一般小于 10ms
    CLK频率为200KHz时，查询次数为30次左右
    */
    for (i = 0; i < SHT30_INQUIRE_CNT; i++)
    {
        // 2. 发起I2C总线启动信号
        i2c_start();
 
        // 3. 发起控制字节，高7bit是地址，bit0是读写控制位，0表示写，1表示读
        i2c_write_byte(addr << 1 | I2C_WRITE);
 
        // 4. 发送一个时钟，判断器件是否正确应答
        if (i2c_wait_ack() == 0)
        {
            break;
        }
    }
    if (i == SHT30_INQUIRE_CNT)
    {
        goto cmd_fail; // 写超时
    }
 
    for (i = 0; i < buff_size; ++i)
    {
        for (j = 0; j < SHT30_INQUIRE_CNT; j++)
        {
            // 5. 发送数据
            i2c_write_byte(write_buff[i]);
 
            // 6. 等待ACK
            if (i2c_wait_ack() == 0)
            {
                break;
            }
        }
 
        if (j == SHT30_INQUIRE_CNT)
        {
            goto cmd_fail; /* 从器件无应答 */
        }
    }
 
    // 7. 发送停止信号
    i2c_stop();
 
    return 0;
 
cmd_fail:
    i2c_stop(); // 发送写超时
    return -1;
}
 
/**
 *  @brief  i2c 发送多个字节
 *  @param  要发送的从机的地址
 *  @param  指向要读取的数据的指针
 *  @param  要读取的数据的长度
 *  @retval 成功返回0，失败返回-1
 */
int i2c_read_bytes(uint8_t addr, uint8_t *read_buff, uint8_t buff_size)
{
    uint16_t i;
 
    for (i = 0; i < SHT30_INQUIRE_CNT; i++)
    {
        /* 1. 发起I2C总线启动信号 */
        i2c_start();
 
        /* 2. 读的话，先写入从机地址 */
        i2c_write_byte(addr << 1 | I2C_WRITE);
 
        /* 3. 发送一个时钟，判断器件是否正确应答 */
        if (i2c_wait_ack() == 0)
        {
            break;
        }
    }
    if (i == SHT30_INQUIRE_CNT)
    {
        goto cmd_fail; // 写超时
    }
 
    for (i = 0; i < SHT30_INQUIRE_CNT; i++)
    {
        /* 4. 重新启动I2C总线。前面的代码的目的是传送地址，下面开始读取数据 */
        i2c_start();
 
        /* 5. 发送读控制 */
        i2c_write_byte(addr << 1 | I2C_READ);
 
        /* 6. 发送一个时钟，判断器件是否正确应答 */
        if (i2c_wait_ack() == 0)
        {
            break;
        }
    }
    if (i == SHT30_INQUIRE_CNT)
    {
        goto cmd_fail; // 写超时
    }
 
    /* 7. 循环读取数据 */
    for (i = 0; i < buff_size; i++)
    {
        read_buff[i] = i2c_read_byte(); /* 读1个字节 */
 
        /* 每读完1个字节后，需要发送Ack， 最后一个字节不需要Ack，发Nack */
        if (i != buff_size - 1)
        {
            i2c_ack(); // 中间字节读完后，CPU产生ACK信号(驱动SDA = 0)
        }
        else
        {
            i2c_Nack(); // 最后1个字节读完后，CPU产生NACK信号(驱动SDA = 1)
        }
    }
 
    /* 8. 发送I2C总线停止信号 */
    i2c_stop();
 
    return 0; /* 执行成功 */
 
cmd_fail:
    i2c_stop(); // 发送写超时
    return -1;
}
 
/**
 *  @brief  I2C初始化
 *  @param  无
 *  @retval 无
 */
void i2c_init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = SHT30_SDA_PIN;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(SHT30_SDA_PORT, &GPIO_InitStruct);  
    GPIO_InitStruct.GPIO_Pin = SHT30_SCL_PIN;
    GPIO_Init(SHT30_SDA_PORT, &GPIO_InitStruct);  

    i2c_stop();
}
 
/**
 *  @brief  读取sht30温湿度传感器的值
 *  @param  指向存储温度值的指针
 *  @param  指向存储湿度值的指针
 *  @retval 成功返回0，失败返回-1
 */
int read_sht30_data(float *temp, float *humi)
{
#ifdef SHT40
    uint8_t writeData[2] = {0xFD,0x06};//{0x2C, 0x06};//SHT40是发送0xFD,SHT30是发送0x2C 0x06
#endif
#ifdef SHT30   
   uint8_t writeData[2] = {0x2C, 0x06};//SHT40是发送0xFD,SHT30是发送0x2C 0x06
#endif       
    uint8_t readData[6] = {0};
    uint8_t retryCount =0 ;
    do
    {
#ifdef SHT40      
        if (i2c_write_bytes(SHT30_ADDR, writeData, 1) == -1)
        {
            return -1;
        }
#endif
#ifdef SHT30      
        if (i2c_write_bytes(SHT30_ADDR, writeData, 2) == -1)
        {
            return -1;
        }
#endif          
        Delay_ms(20);
        if (i2c_read_bytes(SHT30_ADDR, readData, 6) == -1)
        {
            return -1;
        }
        retryCount++;
        if(retryCount>10)
        {
          return -1;
        }
    } while (Crc8(&readData[0], 2) != readData[2] || Crc8(&readData[3], 2) != readData[5]);
 
    *temp = (1.0 * 175 * (readData[0] * 256 + readData[1])) / 65535.0 - 45;
#ifdef SHT40
    *humi = (1.0 * 125 * (readData[3] * 256 + readData[4])) / 65535.0 - 6.0;
#endif
#ifdef SHT30
    *humi = (1.0 * 100 * (readData[3] * 256 + readData[4])) / 65535.0;
#endif
    return 0;
}